/**
 * @format
 * @Author: TC
 * @Date: 2023-03-25 21:04:39
 * @Last Modified by: TC
 * @Last Modified time: 2025-05-05 01:51:51
 */

import { Asset, assetManager, AssetManager } from "cc";
import { app } from "../../App";
import { IBaseInterface } from "../../Interface/BaseInterface";
import { ASSERT_TYPE } from "./ResConst";
import { IResItemInstance } from "./ResUnit";

export class BundleItem implements IBaseInterface {
    private _bundle: AssetManager.Bundle;
    private _mapAssetInLoading: Set<string> = new Set();
    private _mapAsset: Map<string, IResItemInstance> = new Map();
    private _mapcbList: Map<string, Function[]> = new Map();

    constructor(bundle: AssetManager.Bundle) {
        this._bundle = bundle;
    }

    /**
     * 获取bundle
     */
    public get bundle(): AssetManager.Bundle {
        return this._bundle;
    }

    /**
     * 加载资源
     * @param type 类型
     * @param path 路径
     * @param cb 回调
     * @returns
     */
    public loadAsset(type: ASSERT_TYPE, path: string, cb: (asset: Asset) => void): void {
        const info = this.bundle.getInfoWithPath(path);
        if (!info) {
            app.log.error("find info by path error", path);
            return;
        }
        const uuId = info.uuid;
        let loaderItem: IResItemInstance = this._mapAsset.get(uuId);
        //已经加载，直接获取
        if (loaderItem) {
            cb(loaderItem.asset);
            return;
        }
        if (!this._mapcbList.has(path)) {
            this._mapcbList.set(path, []);
        }
        this._mapcbList.get(path).push(cb);
        //正在加载，直接返回
        if (this._mapAssetInLoading.has(path)) {
            return;
        }
        //放入待加载set
        this._mapAssetInLoading.add(path);
        //加载资源
        this.bundle.load(path, Asset, (err, asset) => {
            if (err) {
                app.log.error(err.message);
                return;
            }
            if (this._mapAssetInLoading) this._mapAssetInLoading.delete(path);
            if (!this._mapAsset) {
                return;
            }
            loaderItem = {
                uuId: uuId,
                type: type,
                asset: asset,
            } as unknown as IResItemInstance;
            this._mapAsset.set(uuId, loaderItem);

            this._mapcbList.get(path).forEach((cbFunc) => {
                cbFunc(loaderItem.asset);
            });
            this._mapcbList.get(path).length = 0;
        });
    }

    /**
     * 加载文件夹
     * @param dir
     * @param cb
     * @returns
     */
    public loadDir(dir: string, cb: () => void): void {
        const infoArr = this.bundle.getDirWithPath(dir);
        const totalCnt = infoArr.length;
        let completeCnt = 0;
        if (0 === totalCnt) {
            if (cb) cb();
            return;
        }
        function onComplete(): void {
            completeCnt++;
            if (completeCnt === totalCnt) {
                if (cb) cb();
            }
        }
        for (let i = 0, len = totalCnt; i < len; i++) {
            const type: ASSERT_TYPE = app.resMgr.getAssetType(infoArr[i].ctor as typeof Asset);
            const path: string = infoArr[i].path;
            this.loadAsset(type, path, onComplete);
        }
    }

    /**
     * 释放所有指定类型资源
     * @param type
     */
    public releaseByType(type: ASSERT_TYPE): void {
        this._mapAsset.forEach((item) => {
            if (type === item.type) {
                this.bundle.release(item.uuId);
                this._mapAsset.delete(item.uuId);
            }
        });
    }

    /**
     * 释放bundle内所有资源
     */
    public releaseAll(): void {
        this._mapAsset.forEach((item) => {
            assetManager.releaseAsset(item.asset);
            this._mapAsset.delete(item.uuId);
        });
        this._mapAssetInLoading.clear();
    }

    /**
     * 包内是否有指定资源
     * @param asset
     */
    public hasAsset(asset: Asset): boolean {
        let found = false;
        this._mapAsset.forEach((item) => {
            if (item.asset === asset) {
                found = true;
            }
        });
        return found;
    }

    release(): void {
        this.releaseAll();
        this._mapAsset = null;
        this._mapAssetInLoading = null;
        this._bundle = null;
    }
}
